#include <iostream>
#include <unordered_map>
#include <memory>
#include <vector>

// 模拟一个咖啡店订单系统，其中咖啡口味被视为享元对象，可以被多个订单共享
// 咖啡订单具有外部状态，如客户的偏好，杯子大小等
// 抽象享元接口
class CoffeeFlavor {
    public:
    virtual ~CoffeeFlavor() {}
    virtual void serve(const std::string& extras,const std::string& size) const = 0;
};

// 具体享元类
class ConcreteCoffeeFlavor : public CoffeeFlavor {
    public:
    explicit ConcreteCoffeeFlavor(const std::string& flavorName):flavorName_(flavorName) {} 
    void serve(const std::string& extras,const std::string& size) const override {
        std::cout << "Serving "  << size << " cup of " << flavorName_ 
        << "with extras: " << extras << std::endl;
    }

    private:
    std::string flavorName_;
};

// 享元工厂
class CoffeeFlavorFactory {
    public:
    std::shared_ptr<CoffeeFlavor> getOrder(const std::string& flavor) {
        if(flavors_.find(flavor) == flavors_.end()) {
            // 没有找到就新建一个对象
            flavors_[flavor] = std::make_shared<ConcreteCoffeeFlavor>(flavor);
        }
        return flavors_[flavor]; 
    }
    private:
    std::unordered_map<std::string,std::shared_ptr<CoffeeFlavor>> flavors_;
};

// 订单类，包含外部状态
class CoffeeOrder {
    public:
    CoffeeOrder(std::shared_ptr<CoffeeFlavor> flavor,const std::string& extras,const std::string& size):flavor_(flavor),extras_(extras),size_(size) {}
    void serve() const{
        flavor_->serve(extras_,size_);
    }
    private:
    std::shared_ptr<CoffeeFlavor> flavor_;
    std::string extras_;
    std::string size_;
};

int main () {
    std::cout << "hello，享元模式" << std::endl;
    CoffeeFlavorFactory factory;

    // 创建几个不同的订单，但某些口味会被共享
    std::vector<CoffeeOrder> orders = {
        {factory.getOrder("Espresso"), "no sugar", "small"},
        {factory.getOrder("Latte"), "extra foam", "medium"},
        {factory.getOrder("Espresso"), "double sugar", "large"},
        {factory.getOrder("Cappuccino"), "cinnamon", "medium"},
        {factory.getOrder("Espresso"), "single sugar", "small"}
    };
    for(const auto& order : orders) {
        order.serve();
    }

    return 0;
}